﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CallTimingAnalysis
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        string filePath;
        List<LogEntity> logsEntity;
        private void Form1_Load(object sender, EventArgs e)
        {
            this.dgv_source.DragEnter += file_DragEnter;
            this.dgv_source.DragDrop += file_DragDrop;

            this.dgv_source.RowsDefaultCellStyle.Font = new Font("微软雅黑", 14);
            this.dgv_source.RowsDefaultCellStyle.ForeColor = Color.Gray;
            this.dgv_source.RowTemplate.Height = 28;
            this.dgv_source.CellMouseDown += dgv_right_selected_CellMouseDown;
            this.dgv_source.AllowUserToOrderColumns = true;
            this.dgv_source.DataError += (obj, ea) => { };
            this.dgv_source.SelectionChanged += dgv_source_SelectionChanged;

            this.dataGridView1.RowHeadersVisible = false;
            this.dataGridView1.AutoGenerateColumns = true;
        }

        void dgv_source_SelectionChanged(object sender, EventArgs e)
        {
            var row = this.dgv_source.CurrentRow;
            if (row == null)
                return;
            var log = row.DataBoundItem as LogEntity;
            if (log == null)
                return;
            this.dataGridView1.DataSource = log.Items;
            this.Text = log.url;
        }

        static Regex reg = new Regex(@"(?<date>.*?\.\d{2}:)(?<loginid>.*?)~http(?<url>.*? at ,)(?<d>.*)", RegexOptions.Multiline);


        private List<LogEntity> GetLogToObj(string path)
        {
            Dictionary<string, object> dic;
            return this.GetLogToObj(path, out dic);
        }
        private List<LogEntity> GetLogToObj(string path, out Dictionary<string, object> dic)
        {
            Dictionary<string, object> outt = dic = new Dictionary<string, object>();
            Stopwatch sw_GetLogToObj = new Stopwatch();

            sw_GetLogToObj.Restart();
            var fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            StreamReader sr = new StreamReader(fs);
            var logstr = sr.ReadToEnd();
            outt.Add("Size", (fs.Length / 1024 / 1024) + "MB");
            sr.Dispose();
            fs.Dispose();
            var time = (int)sw_GetLogToObj.ElapsedMilliseconds;
            outt.Add("read", (time / 1000) + "s");
            sw_GetLogToObj.Restart();
            var sel = reg.Matches(logstr).Cast<Match>()
            .Select(p => new LogEntity
            {
                Date = p.Groups["date"].Value,
                LoginId = p.Groups["loginid"].Value,
                url = "http" + p.Groups["url"].Value,
                ItemsStr = p.Groups["d"].Value
            });
            if (this.cbo_option.Checked)
            {
                sel = sel.Where(p => p.Items.Count > 2);
            }
            logsEntity = sel.ToList();
            time = (int)sw_GetLogToObj.ElapsedMilliseconds;
            outt.Add("Match", (time / 1000) + "s");
            sw_GetLogToObj.Restart();
            try
            {
                logsEntity.ForEach(p =>
                    {
                        var x = p.Timing;
                        var xxx = p.Page;
                        xxx = p.PageName;
                    });
            }
            catch (Exception ex)
            {
                MessageBox.Show(path + "////////" + ex.ToString());
            }
            time = (int)sw_GetLogToObj.ElapsedMilliseconds;
            outt.Add("For", (time / 1000) + "s");
            sw_GetLogToObj.Stop();
            return logsEntity;
        }

        #region 文件拖拽
        void file_DragDrop(object sender, DragEventArgs e)
        {
            string[] filePath = (string[])e.Data.GetData(DataFormats.FileDrop, false);
            if (filePath == null || filePath.Length != 1)
            {
                MessageBox.Show("请拖拽一个文件");
                return;
            }
            this.filePath = filePath[0];
        }
        void file_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.Link;
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }
        void dgv_right_selected_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
        {
            var dgv = sender as DataGridView;
            if (e.Button == MouseButtons.Right)
            {
                if (e.RowIndex >= 0)
                {
                    dgv.ClearSelection();
                    dgv.Rows[e.RowIndex].Selected = true;
                    dgv.CurrentCell = dgv.Rows[e.RowIndex].Cells[e.ColumnIndex];
                }
            }
        }
        #endregion

        //单文件显示详情
        private void button1_Click(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(filePath))
            {
                MessageBox.Show("file path is empty");
                return;
            }
            var width = this.dgv_source.Width;

            this.logsEntity = GetLogToObj(this.filePath);
            this.dgv_source.DataSource = logsEntity.ToBindingList();
            this.dgv_source.Columns["ItemsStr"].Visible = false;
            this.dgv_source.Columns["url"].Visible = false;


            //if (width <= 800)
            {
                this.dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
                foreach (DataGridViewColumn item in dgv_source.Columns)
                {
                    if (!item.Visible)
                    {
                        continue;
                    }
                    item.Width = 160;
                }
                dgv_source.Columns["Page"].Width = width - (160 * 4);
            }
        }

        //单文件分析柱图
        private void button2_Click(object sender, EventArgs e)
        {

            if (logsEntity == null)
            {
                MessageBox.Show("logsEntity is null");
                return;
            }
            FileInfo fi = new FileInfo(filePath);
            var form = new tuBiao();
            form.logsEntity_pub = this.logsEntity;
            form.FileName = fi.Name;
            form.Show();
            return;

        }

        //多文件分析柱图
        private async void button3_Click(object sender, EventArgs e)
        {
            if (this.logsEntity == null)
            {
                var fils = GetFiles();
                if (fils.Count == 0)
                    return;

                this.label1.Visible = true;
                this.button3.Enabled = false;

                var data = await GetLogToObjAsync(fils);
                logsEntity = data;
                this.label1.Visible = false;
                this.button3.Enabled = true;
            }
            var form = new tuBiao();
            form.logsEntity_pub = logsEntity;
            form.FileName = "文件集合";
            form.Show();
            this.lb_status.Text = "data 已生成";
        }

        private async Task<List<LogEntity>> GetLogToObjAsync(List<string> fils)
        {
            return await Task.Factory.StartNew(() =>
               {
                   Stopwatch sw = new Stopwatch();
                   sw.Start();

                   List<LogEntity> data = NewMethod(fils);
                   var suuccess = sw.ElapsedMilliseconds / 1000;
                   this.Text = "总记录：" + data.Count + "   耗时：" + suuccess + "s";
                   return data;
               });
        }

        private List<LogEntity> NewMethod(List<string> fils)
        {
            var i = 0;
            List<LogEntity> data //= new List<LogEntity>(3000 * fils.Count);
                = fils.AsParallel()
                 .WithDegreeOfParallelism(4)
                 .WithExecutionMode(ParallelExecutionMode.ForceParallelism)
                 .WithMergeOptions(ParallelMergeOptions.FullyBuffered)
                 .SelectMany(p =>
                 {
                     var threaid = Thread.CurrentThread.ManagedThreadId;
                     Dictionary<string, object> dic;
                     var tmpData = this.GetLogToObj(p, out dic);
                     ++i;
                     var str = i + "/" + fils.Count + ",";
                     foreach (var item in dic)
                     {
                         str += item.Key + ":" + item.Value + ",";
                     }

                     str += "Id:" + threaid;
                     if (!this.InvokeRequired)
                     {
                         this.label1.Text = str;
                     }
                     else
                     {
                         this.Invoke(new Action(() =>
                         {
                             this.label1.Text = str;
                         }));
                     }
                     return tmpData;
                 }).ToList();


            //.ForAll(p =>
            //{
            //    int read = 0, match = 1;
            //    this.Text += Thread.CurrentThread.ManagedThreadId + ",";
            //    var tmpData = this.GetLogToObj(p, out read, out match);
            //    lock (data)
            //    {
            //        data.AddRange(tmpData);
            //        ++i;
            //        this.label1.Text = i + "/" + fils.Count + "||" +
            //            string.Format("read:{0},match:{1},threadId:{2}", read, match, Thread.CurrentThread.ManagedThreadId);
            //    }
            //});
            return data;
        }

        //多文件查询
        private async void button4_Click(object sender, EventArgs e)
        {
            var fils = GetFiles();
            if (fils.Count == 0)
                return;
            this.label1.Visible = true;
            this.button4.Enabled = false;
            var data = await GetLogToObjAsync(fils);

            this.label1.Visible = false;
            this.button4.Enabled = true;
            var form = new GroupView();
            form.Entites = logsEntity = data;
            form.Show();
            this.lb_status.Text = "data 已生成";
        }

        private List<string> GetFiles()
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Multiselect = true;
            ofd.DefaultExt = ".log";
            ofd.Filter = "*.log|";
            if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                return ofd.FileNames.ToList();
            return new List<string>();
        }

        //GC
        private void button5_Click(object sender, EventArgs e)
        {
            GC.Collect();
        }

        //性能改进
        Dictionary<string, Dictionary<string, List<LogEntity>>> file_page_logs = new Dictionary<string, Dictionary<string, List<LogEntity>>>();
        private void button6_Click(object sender, EventArgs e)
        {
            //var files = this.GetFiles();
            //foreach (var item in files)
            //{
            //    int read, match;
            //    var tmpData = this.GetLogToObj(item, out read, out match);
            //    var dic = tmpData.GroupBy(p => p.PageName)
            //        .ToDictionary(p => p.Key, pv => pv.ToList());
            //    file_page_logs.Add(item, dic);
            //}
        }


    }
    public class LogEntity
    {
        public struct ItemEntity
        {
            public string Key { get; set; }
            public decimal Value { get; set; }
            public decimal Timing { get; set; }
        }
        public string Date { get; set; }
        public string LoginId { get; set; }
        public string url { get; set; }

        public string ItemsStr { get; set; }

        //static Regex reg = new Regex(@"(?:.*?\.com)(?<page>.*?) at ");
        //static Regex reg2 = new Regex(@"(?:.*/)(?<name>.*?)(?:\.|\s|\?.*)");
        string _Page;
        Uri uri;
        public string Page
        {
            get
            {
                if (_Page == null)
                {
                    try
                    {
                        _Page = this.url.Substring(0, this.url.Length - 5);
                        uri = new Uri(_Page);
                        _Page = uri.PathAndQuery;
                        //this.url = null;
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(_Page + "||||" + url, ex);
                    }
                }
                return this._Page;
            }
        }

        string _PageName;
        public string PageName
        {
            get
            {
                if (_PageName == null)
                {
                    try
                    {
                        _PageName = uri.AbsolutePath;
                        uri = null;

                        if (_PageName.StartsWith("/detail/js", StringComparison.OrdinalIgnoreCase))
                            return _PageName = "detail";
                        if (_PageName == "/ajax.axd")
                        {
                            var search = "methodName=";
                            var start = this.Page.IndexOf(search);
                            if (start > 0)
                            {
                                var end = this.Page.IndexOf('&', start);
                                return _PageName = this.Page.Substring(start, end - start);
                            }
                        }
                        if (_PageName == "/AjaxHandler.axd")
                        {
                            var search = "method=";
                            var start = this.Page.IndexOf(search);
                            if (start > 0)
                            {
                                var end = this.Page.IndexOf('&', start);
                                return _PageName = this.Page.Substring(start, end - start);
                            }
                        }
                    }
                    catch (Exception x)
                    {
                        throw new Exception(_Page + "||||" + _PageName, x);
                    }

                }
                return _PageName;
            }
        }
        public decimal Timing
        {
            get
            {
                return this.Items.Last().Value;
            }
        }

        //static Regex reg = new Regex(@"(?<qq>.*?)-(?<bb>\d+(?:\.\d+)?)-?", RegexOptions.Multiline);
        List<ItemEntity> _Items;
        public List<ItemEntity> Items
        {
            get
            {
                if (_Items == null)
                {
                    //BeginRequest-0.0000,EndRequest-0.1420
                    //try
                    //{
                    var items = ItemsStr.Split(',');
                    _Items = new List<ItemEntity>(items.Length);
                    for (int i = 0; i < items.Length; i++)
                    {
                        var p = items[i];
                        var item = p.Split('-');
                        var entity = new ItemEntity()
                          {
                              Key = item[0],
                              Value = decimal.Parse(item[1]),
                              Timing = 0
                          };

                        if (i > 0)
                        {
                            entity.Timing = entity.Value - _Items[i - 1].Value;
                        }
                        _Items.Add(entity);
                    }
                    this.ItemsStr = null;
                    //}
                    //catch (Exception ex)
                    //{
                    //    MessageBox.Show(this.Date + this.LoginId + this.url + this.ItemsStr);
                    //}
                }
                return _Items;
            }
        }


    }


}
